using BepuUtilities;
using DemoRenderer;
using BepuPhysics;
using BepuPhysics.Collidables;
using DemoContentLoader;
using BepuPhysics.CollisionDetection;
using BepuPhysics.CollisionDetection.CollisionTasks;
using BepuPhysics.Trees;
using BepuUtilities.Memory;
using BepuUtilities.Collections;
using System.Runtime.CompilerServices;
using DemoRenderer.UI;
using DemoUtilities;
using System;
using BepuPhysics.CollisionDetection.SweepTasks;
#if MYCODE
using BepuUtilities.Vectors;
#else
using System.Numerics;
#endif

namespace Demos.Demos
{
    // 与设计用于特定接口的冲突对相关的类型有多种。
    // 我们不想做一大堆特殊情况,所以我们将实现一些接口。
    // 请注意,我们不仅仅使用ICompoundShape,即使这会减少所需的特例工作量-
    // ICompoundShape设计为在更一般的情况下工作,在这种情况下,每个孩子可能都有唯一的形状。
    // 网格和体素集不是这样的-所有子对象都是单一类型,因此是"同质的"。
    struct Voxels : IHomogeneousCompoundShape<Box, BoxWide>
    {
        // 类型ID在模拟中的所有形状类型中都应该是唯一的。
        public int TypeId => 12;

        // 对于像体素这样高度规则的数据,使用对象空间树不一定是理想的。
        // 我们在这里使用它,因为它已经存在,而专门用于体素的版本不存在。
        // 如果您想最大限度地提高某些特定用例的效率-例如包含
        // ~修改非常快的无限数量的体素-那么您可能想要考虑另一种选择。
        // (如果将数据组织成流式区块,而不是将其组织成块,则基于树的版本仍然可以工作
        // 一个单一的巨型可碰撞的,但它可能不会与一个专门的结构相匹配,因为它需要
        // 利用规则体素栅格的独特属性。)
        public Tree Tree;

        /// <summary>
        /// 三维索引设置的体素集中的体素列表。
        /// </summary>
        public QuickList<Vector3> VoxelIndices;

        /// <summary>
        /// 每个单独体素的大小。更新此值需要更新树叶边界并重新调整。
        /// </summary>
        public Vector3 VoxelSize;

        public int ChildCount => VoxelIndices.Count;

        public Voxels(QuickList<Vector3> voxelIndices, Vector3 voxelSize, BufferPool pool)
        {
            VoxelIndices = voxelIndices;
            VoxelSize = voxelSize;
            Tree = new Tree(pool, voxelIndices.Count);
            // 可以在这里堆叠,但假设可能有相当多的体素。
            // 堆栈很有可能溢出,所以我们转而求助于堆分配。
            pool.Take(voxelIndices.Count, out Buffer<BoundingBox> bounds);
            for (int i = 0; i < voxelIndices.Count; ++i)
            {
                ref var voxel = ref voxelIndices[i];
                ref var voxelBounds = ref bounds[i];
                // 请注意,体素比例已烘焙到树中。这与网格不同,网格允许在不同比例的形状之间共享同一棵树。
                // 如果您愿意,您可以对体素集执行类似的操作;查看网格以获取示例。
                voxelBounds.Min = voxel * VoxelSize;
                voxelBounds.Max = voxelBounds.Min + VoxelSize;
            }
            Tree.SweepBuild(pool, bounds);
            pool.Return(ref bounds);
        }

        public ShapeBatch CreateShapeBatch(BufferPool pool, int initialCapacity, Shapes shapeBatches)
        {
            // 形状类型负责通知形状系统如何为它们创建批次。
            // 凸形将返回ConvexShapeBatch<TShape>,复合形状将返回CompoundShapeBatch<TShape>,
            // 然后我们就有了同质复合物ShapeBatch..。我不喜欢这个名字,但它只是意味着
            // 复合形状的每个子级都具有相同的类型。因此,网格是一种"均质化合物"。
            // 因为它的孩子都是三角形。同样,体素集也是如此,因为每个孩子都只是一个盒子。
            return new HomogeneousCompoundShapeBatch<Voxels, Box, BoxWide>(pool, initialCapacity);
        }

        public void ComputeBounds(in Quaternion orientation, out Vector3 min, out Vector3 max)
        {
            Matrix3x3.CreateFromQuaternion(orientation, out var basis);
            min = new Vector3(float.MaxValue);
            max = new Vector3(float.MinValue);
            for (int i = 0; i < VoxelIndices.Count; ++i)
            {
                var localVoxelPosition = (VoxelIndices[i] + new Vector3(0.5f)) * VoxelSize;
                Matrix3x3.Transform(localVoxelPosition, basis, out var rotatedPosition);
                min = Vector3.Min(rotatedPosition, min);
                max = Vector3.Max(rotatedPosition, max);
            }
            // 所有的孩子都有相同的形状和方向,所以我们可以简单地扩展质心边界框。
            var box = new Box(VoxelSize.X, VoxelSize.Y, VoxelSize.Z);
            box.ComputeBounds(orientation, out var childLocalMin, out var childLocalMax);
            min += childLocalMin;
            max += childLocalMax;
        }

        unsafe struct HitLeafTester<T> : IRayLeafTester where T : IShapeRayHitHandler
        {
            public QuickList<Vector3> VoxelIndices;
            public Vector3 VoxelSize;
            public Box VoxelShape;
            public T HitHandler;
            public Matrix3x3 Orientation;
            public RayData OriginalRay;

            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            public unsafe void TestLeaf(int leafIndex, RayData* ray, float* maximumT)
            {
                ref var voxelIndex = ref VoxelIndices[leafIndex];
                // 请注意,您可以利用体素栅格的规则结构来节省一些处理方向的工作。
                if (VoxelShape.RayTest(new RigidPose((voxelIndex + new Vector3(0.5f)) * VoxelSize), ray->Origin, ray->Direction, out var t, out var normal) && t <= *maximumT)
                {
                    // 将光线法线带回世界空间。
                    Matrix3x3.Transform(normal, Orientation, out normal);
                    // 使用原始光线以避免泄露我们在本地空间工作的事实。
                    HitHandler.OnRayHit(OriginalRay, ref *maximumT, t, normal, leafIndex);
                }
            }
        }

        /// <summary>
        /// 向体素投射光线。对每个考生和每次命中执行回调。
        /// </summary>
        /// <typeparam name="TRayHitHandler">要为每个候选测试执行并命中的回调类型。</typeparam>
        /// <param name="pose">光线测试期间体素的姿势。</param>
        /// <param name="ray">要针对体素进行测试的射线。</param>
        /// <param name="maxumT">射线的最大长度,以射线方向长度为单位。</param>
        /// <param name="hitHandler">对每次命中执行的回调。</param>
        public unsafe void RayTest<TRayHitHandler>(in RigidPose pose, in RayData ray, ref float maximumT, ref TRayHitHandler hitHandler) where TRayHitHandler : struct, IShapeRayHitHandler
        {
            HitLeafTester<TRayHitHandler> leafTester;
            leafTester.VoxelIndices = VoxelIndices;
            leafTester.VoxelSize = VoxelSize;
            leafTester.VoxelShape = new Box(VoxelSize.X, VoxelSize.Y, VoxelSize.Z);
            leafTester.HitHandler = hitHandler;
            Matrix3x3.CreateFromQuaternion(pose.Orientation, out leafTester.Orientation);
            leafTester.OriginalRay = ray;
            Matrix3x3.TransformTranspose(ray.Origin - pose.Position, leafTester.Orientation, out var localOrigin);
            Matrix3x3.TransformTranspose(ray.Direction, leafTester.Orientation, out var localDirection);
            Tree.RayCast(localOrigin, localDirection, ref maximumT, ref leafTester);
            // 树叶测试仪可能变异了命中处理器;把它复制回来。
            hitHandler = leafTester.HitHandler;
        }

        // 某些形状利用多条同时进入的光线来执行批处理。
        // 在某些情况下,这可以大大提高性能。我们不会费心在这里利用它,但是如果您愿意,您可以添加它！
        /// <summary>
        /// 同时向体素投射一束光线,为每个测试候选人和每个命中执行回调。
        /// </summary>
        /// <typeparam name="TRayHitHandler">要为每个射线测试候选对象和每个命中执行的回调类型。</typeparam>
        /// <param name="pose">光线测试期间体素的姿势。</param>
        /// <param name="ray">要针对体素投射的光线集。</param>
        /// <param name="hitHandler">要执行的回调。</param>
        public unsafe void RayTest<TRayHitHandler>(in RigidPose pose, ref RaySource rays, ref TRayHitHandler hitHandler) where TRayHitHandler : struct, IShapeRayHitHandler
        {
            HitLeafTester<TRayHitHandler> leafTester;
            leafTester.VoxelIndices = VoxelIndices;
            leafTester.VoxelSize = VoxelSize;
            leafTester.VoxelShape = new Box(VoxelSize.X, VoxelSize.Y, VoxelSize.Z);
            leafTester.HitHandler = hitHandler;
            Matrix3x3.CreateFromQuaternion(pose.Orientation, out leafTester.Orientation);
            Matrix3x3.Transpose(leafTester.Orientation, out var inverseOrientation);
            for (int i = 0; i < rays.RayCount; ++i)
            {
                rays.GetRay(i, out var ray, out var maximumT);
                leafTester.OriginalRay = *ray;
                Matrix3x3.Transform(ray->Origin - pose.Position, inverseOrientation, out var localOrigin);
                Matrix3x3.Transform(ray->Direction, inverseOrientation, out var localDirection);
                Tree.RayCast(localOrigin, localDirection, ref *maximumT, ref leafTester);
            }
            // 树叶测试仪可能变异了命中处理器;把它复制回来。
            hitHandler = leafTester.HitHandler;
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void GetLocalChild(int childIndex, out Box childShape)
        {
            var halfSize = VoxelSize * 0.5f;
            childShape.HalfWidth = halfSize.X;
            childShape.HalfHeight = halfSize.Y;
            childShape.HalfLength = halfSize.Z;
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void GetPosedLocalChild(int childIndex, out Box childShape, out RigidPose childPose)
        {
            GetLocalChild(childIndex, out childShape);
            childPose = new RigidPose((VoxelIndices[childIndex] + new Vector3(0.5f)) * VoxelSize);
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void GetLocalChild(int childIndex, ref BoxWide shapeWide)
        {
            // 此函数提供对AOSOA结构中车道的引用。
            // 我们要填上第一车道,忽略其他车道。
            var halfSize = VoxelSize * 0.5f;
            GatherScatter.GetFirst(ref shapeWide.HalfWidth) = halfSize.X;
            GatherScatter.GetFirst(ref shapeWide.HalfHeight) = halfSize.Y;
            GatherScatter.GetFirst(ref shapeWide.HalfLength) = halfSize.Z;
        }


        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public unsafe void FindLocalOverlaps<TOverlaps, TSubpairOverlaps>(ref Buffer<OverlapQueryForPair> pairs, BufferPool pool, Shapes shapes, ref TOverlaps overlaps)
             where TOverlaps : struct, ICollisionTaskOverlaps<TSubpairOverlaps>
             where TSubpairOverlaps : struct, ICollisionTaskSubpairOverlaps
        {
            // 只需按顺序遍历每个孩子在树上的位置即可。
            // 这会使大量缓存未命中序列化。您可能会从"同时"遍历所有对中获得一些好处-也就是说,
            // 利用我们有很多独立查询的事实来确保CPU总是有事情要做。但是为了这个演示,我们将以简单的方式进行。

            // 此枚举器所做的全部工作就是获取GetOverlaps函数报告的重叠,并将其添加到重叠列表中。
            ShapeTreeOverlapEnumerator<TSubpairOverlaps> enumerator;
            enumerator.Pool = pool;
            for (int i = 0; i < pairs.Length; ++i)
            {
                ref var pair = ref pairs[i];
                ref var voxelsSet = ref Unsafe.AsRef<Voxels>(pair.Container);
                enumerator.Overlaps = Unsafe.AsPointer(ref overlaps.GetOverlapsForPair(i));
                voxelsSet.Tree.GetOverlaps(pair.Min, pair.Max, ref enumerator);
            }
        }

        public unsafe void FindLocalOverlaps<TOverlaps>(in Vector3 min, in Vector3 max, in Vector3 sweep, float maximumT, BufferPool pool, Shapes shapes, void* overlaps) where TOverlaps : ICollisionTaskSubpairOverlaps
        {
            // 与上面的非扫描FindLocalOverlaps函数类似,这只是将重叠添加到提供的集合。
            // 由于泛型指针的某些语言限制,导致类型信息的一些不幸丢失-假设重叠指针的类型为TOverlaps*。
            // 当C#允许时,它会这样做。
            ShapeTreeSweepLeafTester<TOverlaps> enumerator;
            enumerator.Pool = pool;
            enumerator.Overlaps = overlaps;
            Tree.Sweep(min, max, sweep, maximumT, ref enumerator);
        }

        public void Dispose(BufferPool pool)
        {
            Tree.Dispose(pool);
            VoxelIndices.Dispose(pool);
        }
    }

    // "Continuations"告诉冲突批处理程序在完成批处理后如何处理冲突检测结果。
    // 对于通常的窄相流水线中的简单凸凸对,它们只报告流形来生成约束。
    // 对于更复杂的类型,如化合物,可能有多个子对,每个子对都有自己的接触流形。
    // 这些流形在后处理步骤中组合。对于大多数化合物来说,这是一种"非凸还原"。
    // 还有一个比较复杂的"网格还原"(MeshReduce),它试图平滑三角形边界上的碰撞,以避免在滑动过程中出现凹凸不平的情况。
    // 对于我们的体素集,我们将只使用非凸面还原,尽管它在滑动过程中会允许在形状边界处发生凹凸。
    // (对于积极进取的读者来说,我将体素的边界平滑作为一项不容易的练习。)
    public struct ConvexVoxelsContinuations : IConvexCompoundContinuationHandler<NonconvexReduction>
    {
        public CollisionContinuationType CollisionContinuationType => CollisionContinuationType.NonconvexReduction;

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public ref NonconvexReduction CreateContinuation<TCallbacks>(
            ref CollisionBatcher<TCallbacks> collisionBatcher, int childCount, in BoundsTestedPair pair, in OverlapQueryForPair pairQuery, out int continuationIndex)
            where TCallbacks : struct, ICollisionCallbacks
        {
            return ref collisionBatcher.NonconvexReductions.CreateContinuation(childCount, collisionBatcher.Pool, out continuationIndex);
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static unsafe void GetChildData<TCallbacks>(ref CollisionBatcher<TCallbacks> collisionBatcher, ref NonconvexReductionChild continuationChild,
            in BoundsTestedPair pair, int shapeTypeA, int childIndexB, out RigidPose childPoseB, out int childTypeB, out void* childShapeDataB)
            where TCallbacks : struct, ICollisionCallbacks
        {
            ref var voxels = ref Unsafe.AsRef<Voxels>(pair.B);
            ref var voxelIndex = ref voxels.VoxelIndices[childIndexB];
            var localPosition = (voxelIndex + new Vector3(0.5f)) * voxels.VoxelSize;
            QuaternionEx.TransformWithoutOverlap(localPosition, pair.OrientationB, out childPoseB.Position);
            childPoseB.Orientation = Quaternion.Identity;
            childTypeB = Box.Id;
            // 碰撞批处理程序累积要由指针处理的对,因为在几乎所有其他情况下,形状数据都已由指针可用。
            // 但是在体素集的情况下,我们实际上没有明确的形状(或者像在MeshReductions中那样的辅助存储位置)。
            // 我们不能只是在堆栈上分配一个形状,然后返回一个指向它的指针-数据需要在冲突批处理程序刷新该类型的批处理之前是有效的。
            // 幸运的是,冲突批处理程序公开了一个方便的按对类型的堆分配的内存BLOB,我们可以使用它来存储形状数据。
            // 当碰撞批处理盘被冲洗时,它会自动被清理干净。
            var halfSize = voxels.VoxelSize * 0.5f;
            // 这会将向量3重新解释为要复制到缓存中的长方体,这有点粗略。
            // 形状缓存实际上没有任何类型信息-它只是策略放置的内存。
            // 换句话说,我们只是为这12个字节留出一个空间,直到刷新。
            collisionBatcher.CacheShapeB(shapeTypeA, childTypeB, Unsafe.AsPointer(ref halfSize), 12, out childShapeDataB);
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public unsafe void ConfigureContinuationChild<TCallbacks>(
            ref CollisionBatcher<TCallbacks> collisionBatcher, ref NonconvexReduction continuation, int continuationChildIndex, in BoundsTestedPair pair, int shapeTypeA, int childIndexB,
            out RigidPose childPoseB, out int childTypeB, out void* childShapeDataB)
            where TCallbacks : struct, ICollisionCallbacks
        {
            ref var continuationChild = ref continuation.Children[continuationChildIndex];
            GetChildData(ref collisionBatcher, ref continuationChild, pair, shapeTypeA, childIndexB, out childPoseB, out childTypeB, out childShapeDataB);
            // 冲突处理器期望以特定顺序提供数据。翻转掩码用于确保我们以正确的顺序提供数据。
            // 冲突批处理程序在通过回调报告冲突数据时还会考虑翻转掩码,以保持原始用户顺序。
            if (pair.FlipMask < 0)
            {
                continuationChild.ChildIndexA = childIndexB;
                continuationChild.ChildIndexB = 0;
                continuationChild.OffsetA = childPoseB.Position;
                continuationChild.OffsetB = default;
            }
            else
            {
                continuationChild.ChildIndexA = 0;
                continuationChild.ChildIndexB = childIndexB;
                continuationChild.OffsetA = default;
                continuationChild.OffsetB = childPoseB.Position;
            }
        }
    }

    public unsafe struct CompoundVoxelsContinuations<TCompoundA> : ICompoundPairContinuationHandler<NonconvexReduction>
        where TCompoundA : ICompoundShape
    {
        public CollisionContinuationType CollisionContinuationType => CollisionContinuationType.NonconvexReduction;

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public ref NonconvexReduction CreateContinuation<TCallbacks>(
            ref CollisionBatcher<TCallbacks> collisionBatcher, int totalChildCount, ref Buffer<ChildOverlapsCollection> pairOverlaps, ref Buffer<OverlapQueryForPair> pairQueries, in BoundsTestedPair pair, out int continuationIndex)
            where TCallbacks : struct, ICollisionCallbacks
        {
            return ref collisionBatcher.NonconvexReductions.CreateContinuation(totalChildCount, collisionBatcher.Pool, out continuationIndex);
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public unsafe void GetChildAData<TCallbacks>(ref CollisionBatcher<TCallbacks> collisionBatcher, ref NonconvexReduction continuation, in BoundsTestedPair pair, int childIndexA,
            out RigidPose childPoseA, out int childTypeA, out void* childShapeDataA)
            where TCallbacks : struct, ICollisionCallbacks
        {
            ref var compoundA = ref Unsafe.AsRef<TCompoundA>(pair.A);
            ref var compoundChildA = ref compoundA.GetChild(childIndexA);
            Compound.GetRotatedChildPose(compoundChildA.LocalPose, pair.OrientationA, out childPoseA);
            childTypeA = compoundChildA.ShapeIndex.Type;
            collisionBatcher.Shapes[childTypeA].GetShapeData(compoundChildA.ShapeIndex.Index, out childShapeDataA, out _);
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public unsafe void ConfigureContinuationChild<TCallbacks>(
            ref CollisionBatcher<TCallbacks> collisionBatcher, ref NonconvexReduction continuation, int continuationChildIndex, in BoundsTestedPair pair, int childIndexA, int childTypeA, int childIndexB, in RigidPose childPoseA,
            out RigidPose childPoseB, out int childTypeB, out void* childShapeDataB)
            where TCallbacks : struct, ICollisionCallbacks
        {
            ref var continuationChild = ref continuation.Children[continuationChildIndex];

            ConvexVoxelsContinuations.GetChildData(ref collisionBatcher, ref continuationChild, pair, childTypeA, childIndexB, out childPoseB, out childTypeB, out childShapeDataB);
            if (pair.FlipMask < 0)
            {
                continuationChild.ChildIndexA = childIndexB;
                continuationChild.ChildIndexB = childIndexA;
                continuationChild.OffsetA = childPoseB.Position;
                continuationChild.OffsetB = childPoseA.Position;
            }
            else
            {
                continuationChild.ChildIndexA = childIndexA;
                continuationChild.ChildIndexB = childIndexB;
                continuationChild.OffsetA = childPoseA.Position;
                continuationChild.OffsetB = childPoseB.Position;
            }
        }

    }

    public class CustomVoxelCollidableDemo : Demo
    {
        Voxels voxels;
        StaticHandle handle;
        public unsafe override void Initialize(ContentArchive content, Camera camera)
        {
            camera.Position = new Vector3(-40, 40, -40);
            camera.Yaw = MathHelper.Pi * 3f / 4;
            camera.Pitch = MathHelper.Pi * 0.05f;

            // PositionFirstTimestpper是最简单的时步模式,但由于它在帧开始时将速度积分到位置中,因此直接修改了时间步长之外的速度
            // 将在碰撞检测或解算器有机会介入之前集成。这在这个演示中很好。其他内置选项包括PositionLastTimestpper和SubsteppingTimestpper。
            // 请注意,timeSteper还具有回调,您可以使用这些回调在处理阶段之间执行逻辑,如BeforeCollisionDetection。
            Simulation = Simulation.Create(BufferPool, new DemoNarrowPhaseCallbacks(), new DemoPoseIntegratorCallbacks(new Vector3(0, -10, 0)), new PositionFirstTimestepper());

            // 必须通知窄相位存在新的可碰撞类型。对于我们要支持的每个对类型,必须注册碰撞任务。
            // 所有默认引擎类型在模拟创建时通过调用DefaultTypes.CreateDefaultCollisionTaskRegistry.进行注册
            Simulation.NarrowPhase.CollisionTaskRegistry.Register(new ConvexCompoundCollisionTask<Sphere, Voxels, ConvexCompoundOverlapFinder<Sphere, SphereWide, Voxels>, ConvexVoxelsContinuations, NonconvexReduction>());
            Simulation.NarrowPhase.CollisionTaskRegistry.Register(new ConvexCompoundCollisionTask<Capsule, Voxels, ConvexCompoundOverlapFinder<Capsule, CapsuleWide, Voxels>, ConvexVoxelsContinuations, NonconvexReduction>());
            Simulation.NarrowPhase.CollisionTaskRegistry.Register(new ConvexCompoundCollisionTask<Box, Voxels, ConvexCompoundOverlapFinder<Box, BoxWide, Voxels>, ConvexVoxelsContinuations, NonconvexReduction>());
            Simulation.NarrowPhase.CollisionTaskRegistry.Register(new ConvexCompoundCollisionTask<Triangle, Voxels, ConvexCompoundOverlapFinder<Triangle, TriangleWide, Voxels>, ConvexVoxelsContinuations, NonconvexReduction>());
            Simulation.NarrowPhase.CollisionTaskRegistry.Register(new ConvexCompoundCollisionTask<Cylinder, Voxels, ConvexCompoundOverlapFinder<Cylinder, CylinderWide, Voxels>, ConvexVoxelsContinuations, NonconvexReduction>());
            Simulation.NarrowPhase.CollisionTaskRegistry.Register(new ConvexCompoundCollisionTask<ConvexHull, Voxels, ConvexCompoundOverlapFinder<ConvexHull, ConvexHullWide, Voxels>, ConvexVoxelsContinuations, NonconvexReduction>());

            Simulation.NarrowPhase.CollisionTaskRegistry.Register(new CompoundPairCollisionTask<Compound, Voxels, CompoundPairOverlapFinder<Compound, Voxels>, CompoundVoxelsContinuations<Compound>, NonconvexReduction>());
            Simulation.NarrowPhase.CollisionTaskRegistry.Register(new CompoundPairCollisionTask<BigCompound, Voxels, CompoundPairOverlapFinder<BigCompound, Voxels>, CompoundVoxelsContinuations<BigCompound>, NonconvexReduction>());

            // 请注意,此演示不包括网格-体素和体素-体素对。这些会变得有点复杂,因为在预置的帮助器中有一些空白。
            // 如果您想创建自己的网格,请查看与网格相关的各种类型。它们是一个很好的起点,尽管我对它的复杂性并不是很满意
            // 当前的设计。它们可能会收到一些重大更改-如果您创建的内容严重依赖于它们当前的实现,请记住这一点。

            // 要支持扫描测试,我们还必须注册扫描任务。不需要额外的工作来支持它们;形状上的接口实现已经足够好了。
            Simulation.NarrowPhase.SweepTaskRegistry.Register(new ConvexHomogeneousCompoundSweepTask<Sphere, SphereWide, Voxels, Box, BoxWide, ConvexCompoundSweepOverlapFinder<Sphere, Voxels>>());
            Simulation.NarrowPhase.SweepTaskRegistry.Register(new ConvexHomogeneousCompoundSweepTask<Capsule, CapsuleWide, Voxels, Box, BoxWide, ConvexCompoundSweepOverlapFinder<Capsule, Voxels>>());
            Simulation.NarrowPhase.SweepTaskRegistry.Register(new ConvexHomogeneousCompoundSweepTask<Box, BoxWide, Voxels, Box, BoxWide, ConvexCompoundSweepOverlapFinder<Box, Voxels>>());
            Simulation.NarrowPhase.SweepTaskRegistry.Register(new ConvexHomogeneousCompoundSweepTask<Triangle, TriangleWide, Voxels, Box, BoxWide, ConvexCompoundSweepOverlapFinder<Triangle, Voxels>>());
            Simulation.NarrowPhase.SweepTaskRegistry.Register(new ConvexHomogeneousCompoundSweepTask<Cylinder, CylinderWide, Voxels, Box, BoxWide, ConvexCompoundSweepOverlapFinder<Cylinder, Voxels>>());
            Simulation.NarrowPhase.SweepTaskRegistry.Register(new ConvexHomogeneousCompoundSweepTask<ConvexHull, ConvexHullWide, Voxels, Box, BoxWide, ConvexCompoundSweepOverlapFinder<ConvexHull, Voxels>>());

            Simulation.NarrowPhase.SweepTaskRegistry.Register(new CompoundHomogeneousCompoundSweepTask<Compound, Voxels, Box, BoxWide, CompoundPairSweepOverlapFinder<Compound, Voxels>>());
            Simulation.NarrowPhase.SweepTaskRegistry.Register(new CompoundHomogeneousCompoundSweepTask<BigCompound, Voxels, Box, BoxWide, CompoundPairSweepOverlapFinder<BigCompound, Voxels>>());
            // 支持体素-网格和体素-体素将再次需要稍微多一点的工作,尽管比碰撞任务等效项稍微少一点。


            var widthInVoxels = 40;
            var heightInVoxels = 30;
            var voxelIndices = new QuickList<Vector3>(widthInVoxels * heightInVoxels * widthInVoxels, BufferPool);
            for (int i = 0; i < widthInVoxels; ++i)
            {
                for (int j = 0; j < heightInVoxels; ++j)
                {
                    for (int k = 0; k < widthInVoxels; ++k)
                    {
                        // 为稍微有趣的环境创建一些基于正弦波的噪波。
                        var octave0 = MathF.Cos((i + 78) * 0.8f) + MathF.Cos((j + 37) * 0.8f) + MathF.Cos((k + 131) * 0.8f);
                        var octave1 = MathF.Cos((i + 59) * 0.4f) + MathF.Cos((j + 100) * 0.4f) + MathF.Cos((k + 131) * 0.4f);
                        var octave2 = MathF.Cos((i + 43) * 0.1f) + MathF.Cos((j + 200) * 0.1f) + MathF.Cos((k + 281) * 0.1f);
                        var octave3 = MathF.Cos((i + 647) * 0.025f) + MathF.Cos((j + 1553) * 0.025f) + MathF.Cos((k + 53) * 0.025f);
                        var density = octave0 + octave1 + octave2 + octave3;
                        if (density > 0)
                            voxelIndices.AllocateUnsafely() = new Vector3(i, j, k);
                    }
                }
            }
            voxels = new Voxels(voxelIndices, new Vector3(1, 1, 1), BufferPool);
            handle = Simulation.Statics.Add(new StaticDescription(new Vector3(0, 0, 0), new CollidableDescription(Simulation.Shapes.Add(voxels), 0.1f)));

            var random = new Random(5);
            var shapeToDrop = new Box(1, 1, 1);
            shapeToDrop.ComputeInertia(1, out var shapeToDropInertia);
            var descriptionToDrop = BodyDescription.CreateDynamic(new Vector3(), shapeToDropInertia, new CollidableDescription(Simulation.Shapes.Add(shapeToDrop), 0.1f), new BodyActivityDescription(0.01f));
            for (int i = 0; i < 4096; ++i)
            {
                descriptionToDrop.Pose.Position = new Vector3(15 + 10 * (float)random.NextDouble(), 45 + 150 * (float)random.NextDouble(), 15 + 10 * (float)random.NextDouble());
                Simulation.Bodies.Add(descriptionToDrop);
            }

            Simulation.Statics.Add(new StaticDescription(new Vector3(0, -0.5f, 0), new CollidableDescription(Simulation.Shapes.Add(new Box(300, 1, 300)), 0.1f)));
        }

        public override unsafe void Render(Renderer renderer, Camera camera, Input input, TextBuilder text, Font font)
        {
            // 渲染器没有超级灵活的绘图系统,所以我们将直接添加体素形状。效率不是很高,但很管用！
            var shape = new Box(voxels.VoxelSize.X, voxels.VoxelSize.Y, voxels.VoxelSize.Z);
            var shapeDataPointer = &shape;
            ref var voxelsPose = ref Simulation.Statics.Poses[Simulation.Statics.HandleToIndex[handle.Value]];
            for (int i = 0; i < voxels.ChildCount; ++i)
            {
                var localPose = new RigidPose((voxels.VoxelIndices[i] + new Vector3(0.5f)) * voxels.VoxelSize);
                Compound.GetRotatedChildPose(localPose, voxelsPose.Orientation, out var childPose);
                childPose.Position += voxelsPose.Position;
                renderer.Shapes.AddShape(shapeDataPointer, Box.Id, Simulation.Shapes, ref childPose, new Vector3(0.8f, 0.2f, 0.2f));
            }
            base.Render(renderer, camera, input, text, font);
        }
    }
}


